﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using GameScreen;
using GraphicsServer;
using Model;

using OpenTK.Audio;
using Camera;
using Utils;


namespace GraphicsClient
{
    public partial class FormClient : Form
    {
        Socket _clientSend;
        Socket _clientReceive;
        EndPoint _remoteEndPointSend;
        EndPoint _remoteEndPointReceive;
        private Thread _gameThread;
        private World _curWorld;
        byte[] _data;
        int _recv;
        Boolean _isReceivingStarted = false;

        public FormClient()
        {
            InitializeComponent();
        }

        private void btConnect_Click(object sender, EventArgs e)
        {
            string serverIP = tbServerAddress.Text;

            IPAddress serverIPAddress = null;

            if (string.IsNullOrEmpty(serverIP))
                serverIPAddress = PublisherService.ReturnMachineIP();
            else
                serverIPAddress = IPAddress.Parse(serverIP);

            int serverPort = 8088;

            if (!string.IsNullOrEmpty(tbServerPort.Text))
                serverPort = Convert.ToInt32(tbServerPort.Text);

            _clientReceive = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            _clientSend = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            _remoteEndPointReceive = new IPEndPoint(serverIPAddress, serverPort);
            _remoteEndPointSend = new IPEndPoint(serverIPAddress, serverPort + 1);

            string message = "Subscribe";
            _clientReceive.SendTo(Encoding.ASCII.GetBytes(message), _remoteEndPointReceive);

            if (_isReceivingStarted) return;

            _isReceivingStarted = true;
            _data = new byte[1024];

            var thread1 = new Thread(ReceiveDataFromServer) { IsBackground = true };
            thread1.Start();

            if (_gameThread != null) return;

            _curWorld = new World();
            _curWorld.UpdateCamera += CurWorldUpdateCamera;

            var start = new ThreadStart(() =>
            {
                using (AudioContext context = new AudioContext())
                {
                    SoundUtils.Intialize();
                    using (var game = new Game())
                    {
                        game.LoadWorld(_curWorld);
                        game.SetupWorld();
                        game.Run(30.0);
                    }
                }
            });

            _gameThread = new Thread(start);
            _gameThread.Start();
        }

        void ReceiveDataFromServer()
        {
            EndPoint publisherEndPoint = _clientReceive.LocalEndPoint;
            while (_isReceivingStarted)
            {
                _recv = _clientReceive.ReceiveFrom(_data, ref publisherEndPoint);

                double x = BitConverter.ToDouble(_data, 0);
                double y = BitConverter.ToDouble(_data, 8);
                double z = BitConverter.ToDouble(_data, 16);
                double ang = BitConverter.ToDouble(_data, 24);
                double azm = BitConverter.ToDouble(_data, 32);
                var guid = new Guid(_data.Skip(40).Take(16).ToArray());

                var c = new CameraObject();
                c.Angle.AngleRadians = ang;
                c.Azimuth.AngleRadians = azm;
                c.ObjectGuid = guid;
                c.Position = new Point3D(x, y, z);
                _curWorld.OnUpdateCamera(c);
            }
        }

        void CurWorldUpdateCamera(object sender, CameraEventArgs e)
        {
            using (var mem = new MemoryStream())
            {
                mem.Write(BitConverter.GetBytes(e.Camera.Position.X), 0, 8);
                mem.Write(BitConverter.GetBytes(e.Camera.Position.Y), 0, 8);
                mem.Write(BitConverter.GetBytes(e.Camera.Position.Z), 0, 8);
                mem.Write(BitConverter.GetBytes(e.Camera.Angle.AngleRadians), 0, 8);
                mem.Write(BitConverter.GetBytes(e.Camera.Azimuth.AngleRadians), 0, 8);
                mem.Write(e.Camera.ObjectGuid.ToByteArray(), 0, 16);
                var gmData = mem.ToArray();

                _clientSend.SendTo(gmData, _remoteEndPointSend);
            }
        }

        private void btPing_Click(object sender, EventArgs e)
        {
            string message = "Ping!\r\n";
            _clientSend.SendTo(Encoding.ASCII.GetBytes(message), _remoteEndPointSend);

            tbOutput.Text += "Sent Ping\r\n";
        }

        private void FormClient_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (_gameThread != null)
            {
                _gameThread.Abort();
            }
        }
    }

    public class UpdateData
    {
        public double Angle { get; set; }
        public double Azimuth { get; set; }
        public Point Position { get; set; }
        public Guid ObjectGuid { get; set; }
    }
}
